package com.ouyunc.common.context;

import com.alibaba.ttl.TransmittableThreadLocal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 获取请求属性:ServletRequestAttributes 可以获取HttpServletRequest 等相关信息，注意不要将HttpServletRequest 或 ServletRequestAttributes设置ttl中，因为他们会跟随主线程删除
 * 最终拿不到里面的值，测试过
 * 注意： 这种 ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
 * 方式在多线程/父子线程中的子线程中传递request等属性时 或获取不到。即使在主线程 RequestContextHolder.setRequestAttributes(servletRequestAttributes,true)
 * 设置，也会有问题：问题为=》满足的条件是主线程的等在子线程完成后在结束，才可以保证子线程拿到request 中的信息，注意；
 * 所以使用同一全局封装来传递上下文信息, 并且该方法会在 日志组件的切面中使用设置 request 的上下文信息,注意,
 * 注意在使用线程池时需要使用 ThreadPoolUtil.defaultTtlThreadPool() 或 TtlExecutors.getTtlExecutorService(defaultThreadPool()) 包装后的线程池才可以保证数据传递
 */
public class ThreadLocalContextHolder {
    private static Logger log = LoggerFactory.getLogger(ThreadLocalContextHolder.class);
    // 子线程设置不影响父线程中的设置东西
    private static final TransmittableThreadLocal<Map<String, Object>> threadLocal = new TransmittableThreadLocal(){
        /**
         * 初始化
         * @return
         */
        @Override
        protected Object initialValue() {
            return new ConcurrentHashMap<>();
        }
    };

    /**
     * 设置值
     * @param key
     * @param value
     */
    public static void set(String key, Object value) {
        threadLocal.get().put(key, value);
    }

    /**
     * 批量设置值
     * @param keyValueMap
     */
    public static void setBatch(Map<String, Object> keyValueMap) {
        threadLocal.get().putAll(keyValueMap);
    }

    /**
     * 获取 值
     * @return
     */
    public static<T> T get(String key) {
        return (T) threadLocal.get().get(key);
    }

    /**
     * 获取 HttpServletRequest
     * @return
     */
    public static Map<String, Object> getThreadLocal() {
        return threadLocal.get();
    }

    /**
     * 移除某个值
     * @return
     */
    public static<T> T remove(String key) {
        return (T) threadLocal.get().remove(key);
    }

    /**
     * 移除所有键值对
     * @return
     */
    public static void clear() {
        threadLocal.get().clear();
    }
}
